﻿using System;
using System.Linq;
using System.Drawing;
using System.Xml.Linq;
using System.Windows.Forms;
using System.Globalization;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;

namespace System.Custom
{
   [Flags]
   public enum TextDataTypes
   {
      None = 0,
      Url = 1,
      Email = 2,
      Digit = 4,
      Number = 8,
      Phone = 16,
   }

   public static class Utils
   {
      public static object[] AsArray(this object obj)
      {
         object[] arr = { obj };

         return arr;
      }
      
      public static bool Between(this int n, int x1, int x2)
      {
         return n >= x1 && n < x2;
      }

      public static Collection<string> ExtractLinks(string text)
      {
         Collection<string> links = new Collection<string>();

         foreach (Match m in Regex.Matches(text, TextSvc.UrlPattern, RegexOptions.IgnoreCase)) links.Add(m.Value);

         return links;
      }
   }

   public static class DelayedAction
   {
      static Dictionary<object, System.Windows.Forms.Timer> FDelayedActionTimers = new Dictionary<object, System.Windows.Forms.Timer>();

      public static void Do(Action action, int milliseconds)
      {
         if (milliseconds <= 0)
         {
            action();
            return;
         }

         System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
         timer.Interval = milliseconds;
         timer.Tick += (s, e) =>
         {
            timer.Dispose();
            action();
         };
         timer.Start();
      }
      public static void Do(object target, Action action, int milliseconds, bool putoffSequenceOfCall)
      {
         if (milliseconds <= 0)
         {
            action();
            return;
         }

         System.Windows.Forms.Timer timer = null;

         FDelayedActionTimers.TryGetValue(target, out timer);

         if (timer == null)
         {
            timer = new System.Windows.Forms.Timer();

            timer.Enabled = false;
            timer.Interval = milliseconds;
            FDelayedActionTimers[target] = timer;

            timer.Tick += (s, e) =>
            {
               FDelayedActionTimers.Remove(target);
               timer.Dispose();
               action();
            };
         }

         if (putoffSequenceOfCall) timer.Stop();

         if (!timer.Enabled)
         {
            timer.Interval = milliseconds;
            timer.Start();
         }
      }
      public static void Putoff(object target, int milliseconds)
      {
         System.Windows.Forms.Timer timer = null;

         if (target != null && FDelayedActionTimers.TryGetValue(target, out timer))
         {
            timer.Stop();

            if (milliseconds > 0) timer.Interval = milliseconds;

            timer.Start();
         }
      }
   }

   public static class StringExt
   {
      public static int CompareWidth(string s1, string s2, Font font)
      {
         return TextRenderer.MeasureText(s1, font).Width - TextRenderer.MeasureText(s2, font).Width;
      }
      public static string Join(IEnumerable<string> array, string delim) // Analog of string.Join, but accepting IEnumerable instead of Array
      {
         string result = "";

         foreach (string str in array) result += str + delim;

         return result.Length > 0 && delim.Length > 0 ? result.Remove(result.Length - delim.Length) : result;
      }

      public static string TrimIgnoreNull(this string s)
      {
         if (s == null) return "";
         return s.Trim();
      }
      public static bool EqualsIgnoreNull(this string s, string str)
      {
         return s.Equals(str == null ? "" : str);
      }
      public static string SubstrAfter(this string s, string delimeter)
      {
         if (s == null) return "";
         return s.Contains(delimeter) ? s.Substr(s.IndexOf(delimeter) + delimeter.Length, s.Length) : "";
      }
      public static string SubstrBefore(this string s, string delimeter)
      {
         if (s == null) return "";
         return s.Substr(0, s.IndexOf(delimeter));
      }
      public static string SubstrAfterLast(this string s, string delimeter)
      {
         if (s == null) return "";
         return s.Contains(delimeter) ? s.Substr(s.LastIndexOf(delimeter) + delimeter.Length, s.Length) : "";
      }
      public static string SubstrBeforeLast(this string s, string delimeter)
      {
         if (s == null) return "";
         return s.Substr(0, s.LastIndexOf(delimeter));
      }
      public static string Substr(this string s, int startIndex, int length)
      {
         if (s == null) return "";

         if (length < 0) length = s.Length;
         if (startIndex < 0) startIndex = 0;
         if (startIndex >= s.Length) return "";

         return s.Substring(startIndex, startIndex + length < s.Length ? length : s.Length - startIndex);
      }
   }

   public static class TextSvc
   {
      static string FAllowedControlChars = "\r \n \t |";

      public static readonly string UrlPattern = @"(http://[^\s\t\r\n]+|https://[^\s\t\r\n]+|www.[^\s\t\r\n]+)" +
                                                 @"|(mailto://\w+)@(\w+)";

      public static readonly string PhonePattern = @"\b\d{1,4}-\d{1,4}(-)*\d{1,4}\b";

      public static bool IsReadable(char c)
      {
         UnicodeCategory type = char.GetUnicodeCategory(c);

         return FAllowedControlChars.Contains(c) ||
                !(type == UnicodeCategory.Control ||
                  type == UnicodeCategory.Format ||
                  type == UnicodeCategory.OtherSymbol ||
                  type == UnicodeCategory.OtherNotAssigned ||
                  type == UnicodeCategory.Surrogate);
      }
      public static bool IsReadable(string text)
      {
         return string.IsNullOrEmpty(text) == false &&
            (text.Length > 15 ? text.ToCharArray(0, 15) : text.ToCharArray()).Count(c =>
             char.GetUnicodeCategory(c) == UnicodeCategory.OtherSymbol) / (text.Length > 15 ? 15 : text.Length) < 0.5;
      }
      public static bool HasHtml(string text)
      {
         if (string.IsNullOrEmpty(text)) return false;

         text = text.ToLower();
         
         return text.StartsWith("<font") && text.EndsWith("</font>") ||
                text.StartsWith("<html><body") && text.EndsWith("</body></html>");
      }
      public static string GetHtml(string text)
      {
         return Regex.Replace(text, UrlPattern, new MatchEvaluator((m) =>
                {
                   return @"<a href='#'>" + m.Value + "</a>";
                }),
                RegexOptions.IgnoreCase);
      }
      public static string RemoveHtml(string text)
      {
         text = text.Replace("<BR>", "\r\n").Replace("<br>", "\r\n");
         return HasHtml(text) ? text.Replace("><", "").SubstrAfter(">").SubstrBefore("<").TrimIgnoreNull() : text;
      }
      public static bool Contains(string text, string s)
      {
         return Regex.IsMatch(text, "(" + Regex.Escape(s) + ")", RegexOptions.IgnoreCase);
      }
      public static bool ContainsPattern(string text, string s)
      {
         return Regex.IsMatch(text, s, RegexOptions.IgnoreCase);
      }
      public static bool Contains(string text, TextDataTypes ct)
      {
         int count = 0;

         if ((ct & TextDataTypes.Url) == TextDataTypes.Url)
         {
            if (Regex.IsMatch(text, UrlPattern, RegexOptions.IgnoreCase)) count++;
         }
         if ((ct & TextDataTypes.Email) == TextDataTypes.Email)
         {
            if (Regex.IsMatch(text, @"(\w+)@(\w+)")) count++;
         }

         text = Regex.Replace(text, UrlPattern, " ", RegexOptions.IgnoreCase);

         if ((ct & TextDataTypes.Digit) == TextDataTypes.Digit)
         {
            if (Regex.IsMatch(text, @"\b[0-9]+\b")) count++;
         }
         if ((ct & TextDataTypes.Number) == TextDataTypes.Number)
         {
            if (Regex.IsMatch(text, @"\b[0-9]{5,20}\b")) count++;
         }
         if ((ct & TextDataTypes.Phone) == TextDataTypes.Phone)
         {
            if (Regex.IsMatch(text, PhonePattern)) count++;
         }
         return count > 0;
      }
   }
}
